package com.novellotus.iov.carvice.maps;

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.TextView;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.animation.AlphaAnimation;
import com.amap.api.maps.model.animation.Animation;
import com.novellotus.iov.carvice.bean.MapCarListBean;
import com.novellotus.iov.carvice.bean.MqttListCarBean;
import com.novellotus.iov.carvice.utils.MyUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * Created by yiyi.qi on 16/10/10.
 * 整体设计采用了两个线程,一个线程用于计算组织聚合数据,一个线程负责处理Marker相关操作
 */
public class ClusterOverlay implements
        AMap.OnMarkerClickListener {
    private AMap mAMap;
    private Context mContext;
    private List<MapCarListBean> mClusterItems;//所有车辆的数据
    private List<Cluster> mClusters;//当前地图上聚合的点
    private int mClusterSize;//聚合点的大小
    private ClusterClickListener mClusterClickListener;
    private ClusterRender mClusterRender;
    private List<Marker> mAddMarkers = new ArrayList<>();//地图上所有的车辆
    private double mClusterDistance;//两点聚合的距离
    private LruCache<Integer, BitmapDescriptor> mLruCache;
    private HandlerThread mMarkerHandlerThread = new HandlerThread("addMarker");//添加锚点
    private HandlerThread mSignClusterThread = new HandlerThread("calculateCluster");//计算聚合点
    private Handler mMarkerhandler;
    private Handler mSignClusterHandler;
    private float mPXInMeters;
    private boolean mIsCanceled = false;

    /**
     * 构造函数
     *
     * @param amap
     * @param clusterSize 聚合范围的大小（指点像素单位距离内的点会聚合到一个点显示）
     * @param context
     */
    public ClusterOverlay(AMap amap, int clusterSize, Context context) {
        this(amap, null, clusterSize, context);


    }

    /**
     * 构造函数,批量添加聚合元素时,调用此构造函数
     *
     * @param amap
     * @param clusterItems 聚合元素
     * @param clusterSize
     * @param context
     */
    public ClusterOverlay(AMap amap, List<MapCarListBean> clusterItems,
                          int clusterSize, Context context) {
        //默认最多会缓存80张图片作为聚合显示元素图片,根据自己显示需求和app使用内存情况,可以修改数量
        mLruCache = new LruCache<Integer, BitmapDescriptor>(100) {
            protected void entryRemoved(boolean evicted, Integer key, BitmapDescriptor oldValue, BitmapDescriptor newValue) {
                if (oldValue.getBitmap() != null)
                    oldValue.getBitmap().recycle();
            }
        };
        if (clusterItems != null) {
            mClusterItems = clusterItems;
        } else {
            mClusterItems = new ArrayList<>();
        }
        mContext = context;
        mClusters = new ArrayList<>();
        this.mAMap = amap;
        mClusterSize = clusterSize;
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        amap.setOnMarkerClickListener(this);
        initThreadHandler();
        assignClusters();
    }

    /**
     * 设置聚合点的点击事件
     *
     * @param clusterClickListener
     */
    public void setOnClusterClickListener(
            ClusterClickListener clusterClickListener) {
        mClusterClickListener = clusterClickListener;
    }

    /**
     * 添加一个聚合点
     *
     * @param item
     */
    public void addClusterItem(List<MapCarListBean> item) {

        Message message = Message.obtain();
        message.what = SignClusterHandler.CALCULATE_CLUSTER;
        message.obj = item;
        mSignClusterHandler.sendMessage(message);
    }

    /**
     * 设置聚合元素的渲染样式，不设置则默认为气泡加数字形式进行渲染
     *
     * @param render
     */
    public void setClusterRenderer(ClusterRender render) {
        mClusterRender = render;
    }

    /**
     * 初始化Handler
     */
    private void initThreadHandler() {
        mMarkerHandlerThread.start();
        mSignClusterThread.start();
        mMarkerhandler = new MarkerHandler(mMarkerHandlerThread.getLooper());
        mSignClusterHandler = new SignClusterHandler(mSignClusterThread.getLooper());
    }


    /**
     * 地图移动后重新进行聚合
     */
    public void onCameraChangeFinish() {
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        assignClusters();
    }


    //点击事件
    @Override
    public boolean onMarkerClick(Marker arg0) {
        if (mClusterClickListener == null) {
            return true;
        }
        Cluster cluster = (Cluster) arg0.getObject();
        if (cluster != null) {
            mClusterClickListener.onClick(arg0, cluster.getClusterItems());
            return true;
        }
        return false;
    }


    /**
     * 将聚合元素添加至地图上
     */
    private void addClusterToMap(List<Cluster> clusters) {

        ArrayList<Marker> removeMarkers = new ArrayList<>();
        removeMarkers.addAll(mAddMarkers);
        mAddMarkers.clear();
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        MyAnimationListener myAnimationListener = new MyAnimationListener(removeMarkers);
        for (Marker marker : removeMarkers) {
            marker.setAnimation(alphaAnimation);
            marker.setAnimationListener(myAnimationListener);
            marker.startAnimation();
        }
        for (Cluster cluster : clusters) {
            addSingleClusterToMap(cluster);
        }
    }

    private AlphaAnimation mADDAnimation = new AlphaAnimation(0, 1);

    /**
     * 将单个聚合元素添加至地图显示
     * 聚合点的
     *
     * @param cluster
     */
    private void addSingleClusterToMap(Cluster cluster) {

        LatLng latlng = cluster.getCenterLatLng();
        MarkerOptions markerOptions = new MarkerOptions();
        int carStatue = 0;
        int carType = MyUtils.carType(cluster.getClusterItems().get(0).getOnlineStatus(), cluster.getClusterItems().get(0).getChargeStatus(), cluster.getClusterItems().get(0).getAlarmStatus());
        if (cluster.getClusterItems().size() == 1) {
            if (carType == 0) {
                if (clickVin.equals(cluster.getClusterItems().get(0).getVin())) {
                    carStatue = 10;
                } else {
                    carStatue = 0;
                }
            } else {
                carStatue = MyUtils.carDetailType(cluster.getClusterItems().get(0).getOnlineStatus(), cluster.getClusterItems().get(0).getChargeStatus(), cluster.getClusterItems().get(0).getAlarmStatus());
                if (clickVin.equals(cluster.getClusterItems().get(0).getVin())) {
                    switch (carStatue) {
                        case 0:
                            carStatue = 10;
                            break;
                        case 1:
                            carStatue = 11;
                            break;
                        case 2:
                            carStatue = 12;
                            break;
                        case 3:
                            carStatue = 13;
                            break;
                        case 4:
                            carStatue = 14;
                            break;
                        case 5:
                            carStatue = 15;
                            break;
                    }
                }
            }

        }

        markerOptions.anchor(0.5f, 0.5f).icon(getBitmapDes(cluster.getClusterCount(), carStatue)).position(latlng);
        Marker marker = mAMap.addMarker(markerOptions);
        marker.setAnimation(mADDAnimation);
        marker.setObject(cluster);
        marker.startAnimation();
        cluster.setMarker(marker);
        mAddMarkers.add(marker);

    }

    /**
     * @param mqttListCarBean 收到mqtt的数据进行刷新
     *                        /**
     *                        chargeStatus : 3
     *                        vehicleStatus : 1
     *                        alarmStatus : 3
     *                        onlineStatus : 1
     *                        vin : LGHF9CRH6EJ001731
     *                        pn : 贵A503D0
     *                        logo : carlogo/1498793556624_file.jpg
     *                        name : S-CAR-043
     *                        code : S-CODE-043
     *                        lat : 30.558692
     *                        lng : 104.068374
     *                        collectTime : 1503044537000
     */
    public void updateMqttCar(MqttListCarBean mqttListCarBean, String vin) {
        MapCarListBean mqttBean = null;
        //用mqtt数据更新原数据
        for (MapCarListBean m : mClusterItems) {
            if (mqttListCarBean.getVin().equals(m.getVin())) {
                m.setChargeStatus(mqttListCarBean.getChargeStatus());
                m.setVehicleStatus(mqttListCarBean.getVehicleStatus());
                m.setAlarmStatus(mqttListCarBean.getAlarmStatus());
                m.setOnlineStatus(mqttListCarBean.getOnlineStatus());
                m.setLat(mqttListCarBean.getLat());
                m.setLng(mqttListCarBean.getLng());
                mqttBean = m;
            }
        }
        //刷新单个Marker
        for (Cluster c : mClusters) {
            if (c.getClusterItems().size() == 1 && c.getClusterItems().get(0).getVin().equals(mqttListCarBean.getVin())) {
                LatLng latlng = MyUtils.fromGpsToAmap(mqttListCarBean.getLat(), mqttListCarBean.getLng(), mContext);
                c.setCenterLatLng(latlng);
                if (mqttBean != null) {
                    c.getClusterItems().remove(0);
                    c.getClusterItems().add(mqttBean);
                }
                updateCluster(c);
            }

        }
    }

    /**
     * @param vin
     * 标记当前点击的Marker
     */
    private String clickVin = "";

    public void setClickVin(String vin) {
        clickVin = vin;
    }

    public String getClickVin() {
        return clickVin;
    }

    /**
     * 清除之前的点，设置新的点
     */
    private void calculateClusters() {

        mIsCanceled = false;
        mClusters.clear();
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        for (MapCarListBean clusterItem : mClusterItems) {
            if (mIsCanceled) {
                return;
            }
            LatLng latlng = MyUtils.fromGpsToAmap(clusterItem.getLat(), clusterItem.getLng(), mContext);
            if (visibleBounds.contains(latlng)) {
                Cluster cluster = getCluster(latlng, mClusters);
                if (cluster != null) {
                    cluster.addClusterItem(clusterItem);
                } else {
                    cluster = new Cluster(latlng);
                    mClusters.add(cluster);
                    cluster.addClusterItem(clusterItem);
                }
            }
        }

        //复制一份数据，规避同步
        List<Cluster> clusters = new ArrayList<>();
        clusters.addAll(mClusters);
        Message message = Message.obtain();
        message.what = MarkerHandler.ADD_CLUSTER_LIST;
        message.obj = clusters;
        if (mIsCanceled) {
            return;
        }
        mMarkerhandler.sendMessage(message);
    }

    /**
     * 对点进行聚合
     */
    private void assignClusters() {
        mIsCanceled = true;
        mSignClusterHandler.removeMessages(SignClusterHandler.CALCULATE_CLUSTER);
        mSignClusterHandler.sendEmptyMessage(SignClusterHandler.CALCULATE_CLUSTER);
    }


    /**
     * 在已有的聚合基础上，对添加的单个元素进行聚合
     *
     * @param clusterItem
     */
    private void calculateSingleCluster(MapCarListBean clusterItem) {
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        LatLng latlng = MyUtils.fromGpsToAmap(clusterItem.getLat(), clusterItem.getLng(), mContext);
        if (!visibleBounds.contains(latlng)) {
            return;
        }
        Cluster cluster = getCluster(latlng, mClusters);
        if (cluster != null) {
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.UPDATE_SINGLE_CLUSTER;
            message.obj = cluster;
            mMarkerhandler.removeMessages(MarkerHandler.UPDATE_SINGLE_CLUSTER);
            mMarkerhandler.sendMessageDelayed(message, 5);

        } else {

            cluster = new Cluster(latlng);
            mClusters.add(cluster);
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.ADD_SINGLE_CLUSTER;
            message.obj = cluster;
            mMarkerhandler.sendMessage(message);

        }
    }

    /**
     * 根据一个点获取是否可以依附的聚合点，没有则返回null
     *
     * @param latLng
     * @return
     */
    private Cluster getCluster(LatLng latLng, List<Cluster> clusters) {
        for (Cluster cluster : clusters) {
            LatLng clusterCenterPoint = cluster.getCenterLatLng();
            double distance = AMapUtils.calculateLineDistance(latLng, clusterCenterPoint);
            if (distance < mClusterDistance) {
                return cluster;
            }
        }
        return null;
    }


    /**
     * 获取每个聚合点的绘制样式
     */
    private BitmapDescriptor getBitmapDes(int num, int statue) {
        BitmapDescriptor bitmapDescriptor;
        // if (bitmapDescriptor == null) {
        TextView textView = new TextView(mContext);
        if (num > 1) {
            String tile = String.valueOf(num);
            textView.setText(tile);
        }
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(Color.WHITE);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
        textView.setBackgroundDrawable(mClusterRender.getDrawAble(num, statue));
        if (mClusterRender != null && mClusterRender.getDrawAble(num, statue) != null) {

        } else {
            //  textView.setBackgroundResource(R.mipmap.icon_map_car_run);
        }
        bitmapDescriptor = BitmapDescriptorFactory.fromView(textView);
        //  mLruCache.put(num, bitmapDescriptor);
        //   }
        return bitmapDescriptor;
    }

    /**
     * 更新已加入地图聚合点的样式
     */
    private void updateCluster(Cluster cluster) {
        int carStatue = 0;
        if (cluster.getClusterItems().size() == 1) {
            carStatue = MyUtils.carDetailType(cluster.getClusterItems().get(0).getOnlineStatus(), cluster.getClusterItems().get(0).getChargeStatus(), cluster.getClusterItems().get(0).getAlarmStatus());
            if (cluster.getClusterItems().get(0).getVin().equals(clickVin)) {
                switch (carStatue) {
                    case 0:
                        carStatue = 10;
                        break;
                    case 1:
                        carStatue = 11;
                        break;
                    case 2:
                        carStatue = 12;
                        break;
                    case 3:
                        carStatue = 13;
                        break;
                    case 4:
                        carStatue = 14;
                        break;
                    case 5:
                        carStatue = 15;
                        break;
                }
            }
        }
        Marker marker = cluster.getMarker();
        marker.setIcon(getBitmapDes(cluster.getClusterCount(), carStatue));
        LatLng latlng = MyUtils.fromGpsToAmap(cluster.getClusterItems().get(0).getLat(), cluster.getClusterItems().get(0).getLng(), mContext);
        marker.setPosition(latlng);
    }


//-----------------------辅助内部类用---------------------------------------------

    /**
     * marker渐变动画，动画结束后将Marker删除
     */
    class MyAnimationListener implements Animation.AnimationListener {
        private List<Marker> mRemoveMarkers;

        MyAnimationListener(List<Marker> removeMarkers) {
            mRemoveMarkers = removeMarkers;
        }

        @Override
        public void onAnimationStart() {
        }

        @Override
        public void onAnimationEnd() {
            for (Marker marker : mRemoveMarkers) {
                marker.remove();
            }
            mRemoveMarkers.clear();
        }
    }

    /**
     * 处理market添加，更新等操作
     */
    class MarkerHandler extends Handler {

        static final int ADD_CLUSTER_LIST = 0;

        static final int ADD_SINGLE_CLUSTER = 1;

        static final int UPDATE_SINGLE_CLUSTER = 2;

        MarkerHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {

            switch (message.what) {
                case ADD_CLUSTER_LIST:
                    //添加聚合点列表
                    List<Cluster> clusters = (List<Cluster>) message.obj;
                    addClusterToMap(clusters);
                    break;
                case ADD_SINGLE_CLUSTER:
                    //添加单个聚合点
                    Cluster cluster = (Cluster) message.obj;
                    addSingleClusterToMap(cluster);
                    break;
                case UPDATE_SINGLE_CLUSTER:
                    Cluster updateCluster = (Cluster) message.obj;
                    updateCluster(updateCluster);
                    break;
            }
        }
    }

    /**
     * 处理聚合点算法线程
     */
    class SignClusterHandler extends Handler {
        static final int CALCULATE_CLUSTER = 0;
        static final int CALCULATE_SINGLE_CLUSTER = 1;

        SignClusterHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {
            switch (message.what) {
                case CALCULATE_CLUSTER:
                    calculateClusters();
                    break;
                case CALCULATE_SINGLE_CLUSTER:
                    MapCarListBean item = (MapCarListBean) message.obj;
                    mClusterItems.add(item);
                    Log.i("yiyi.qi", "calculate single cluster");
                    calculateSingleCluster(item);
                    break;
            }
        }
    }

    public void onDestroy() {
        mIsCanceled = true;
        mSignClusterHandler.removeCallbacksAndMessages(null);
        mMarkerhandler.removeCallbacksAndMessages(null);
        mSignClusterThread.quit();
        mMarkerHandlerThread.quit();
        for (Marker marker : mAddMarkers) {
            marker.remove();

        }
        mAddMarkers.clear();
        mLruCache.evictAll();
    }
}